/*
 * Copyright (c) 2025 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import { ArkFile, AstTreeUtils, ts } from "arkanalyzer";
import Logger, { LOG_MODULE_TYPE } from 'arkanalyzer/lib/utils/logger';
import { BaseChecker, BaseMetaData } from "../BaseChecker";
import { FileMatcher, MatcherCallback, MatcherTypes } from '../../Index';
import { Defects, IssueReport } from "../../model/Defects";
import { Rule } from "../../model/Rule";
import { RuleListUtil } from "../../utils/common/DefectsList";
import { RuleFix } from '../../model/Fix';

const logger = Logger.getLogger(LOG_MODULE_TYPE.HOMECHECK, 'NoUnnecessaryQualifierCheck');
const gMetaData: BaseMetaData = {
    severity: 2,
    ruleDocPath: "docs/no-unnecessary-qualifier-check.md",
    description: "Disallow unnecessary namespace qualifiers.",
};

// 定义一个接口，用于存储问题的行列信息
interface LocationInfo {
    fileName: string;
    line: number;
    startCol: number;
    endCol: number;
    Qualifier: string;
}

export class NoUnnecessaryQualifierCheck implements BaseChecker {
    readonly metaData: BaseMetaData = gMetaData;
    public rule: Rule;
    public defects: Defects[] = [];
    public issues: IssueReport[] = [];

    private fileMatcher: FileMatcher = {
        matcherType: MatcherTypes.FILE,
    };

    public registerMatchers(): MatcherCallback[] {
        const fileMatcherCb: MatcherCallback = {
            matcher: this.fileMatcher,
            callback: this.check
        };
        return [fileMatcherCb];
    }

    public check = (arkFile: ArkFile) => {
        if (arkFile instanceof ArkFile) {
            const code = arkFile.getCode();
            if (!code) {
                return;
            }
            const filePath = arkFile.getFilePath();
            const asRoot = AstTreeUtils.getASTNode(arkFile.getName(), code);
            // 检查命名空间限定符
            const program = this.createProgram(filePath);
            const locations = this.checkUnnecessaryQualifiers(asRoot, program);
            this.filteredLocations(locations).forEach((loc) => {
                this.addIssueReportNodeFix(loc, filePath);
            });
        }
    }

    // 检查不必要的限定符
    private checkUnnecessaryQualifiers(sourceFile: ts.SourceFile, program: ts.Program): LocationInfo[] {
        const results: LocationInfo[] = [];
        const typeChecker = program.getTypeChecker();

        // 遍历所有源文件
        program.getSourceFiles().forEach((sourceFile) => {
            if (!sourceFile.isDeclarationFile) {
                const visit = (node: ts.Node, currentNamespace?: string[]) => {
                    // 检查 QualifiedName 和 PropertyAccessExpression
                    if (ts.isQualifiedName(node) || ts.isPropertyAccessExpression(node)) {
                        let leftName: string | undefined;
                        let rightName: string | undefined;

                        if (ts.isQualifiedName(node)) {
                            leftName = node.left.getText(sourceFile);
                            rightName = node.right.getText(sourceFile);
                        } else if (ts.isPropertyAccessExpression(node)) {
                            leftName = node.expression.getText(sourceFile);
                            rightName = node.name.getText();
                        }

                        if (leftName && rightName) {
                            // 获取右侧符号
                            const rightSymbol = typeChecker.getSymbolAtLocation(
                                ts.isQualifiedName(node) ? node.right : node.name
                            );
                            if (rightSymbol) {
                                // 检查右侧符号是否可以在当前作用域直接访问
                                const currentScopeSymbols = this.getSymbolsInCurrentScope(node, sourceFile, typeChecker, currentNamespace);
                                if (this.canBeReferencedWithoutQualifier(rightSymbol, currentScopeSymbols, currentNamespace)) {
                                    const { line, character } = sourceFile.getLineAndCharacterOfPosition(node.getStart());
                                    const endCharacter = character + leftName.length;
                                    results.push({
                                        fileName: sourceFile.fileName,
                                        line: line + 1,
                                        startCol: character + 1,
                                        endCol: endCharacter + 1,
                                        Qualifier: rightName
                                    });
                                }
                            }
                        }
                    }

                    // 处理命名空间
                    if (ts.isModuleDeclaration(node)) {
                        const namespaceName = node.name.getText(sourceFile);
                        const newNamespace = currentNamespace ? [...currentNamespace, namespaceName] : [namespaceName];
                        ts.forEachChild(node, child => visit(child, newNamespace));
                    } else {
                        ts.forEachChild(node, child => visit(child, currentNamespace));
                    }
                };
                visit(sourceFile);
            }
        });

        return results;
    }

    // 获取当前作用域内的所有符号，包括命名空间内部的符号
    private getSymbolsInCurrentScope(node: ts.Node, sourceFile: ts.SourceFile, typeChecker: ts.TypeChecker, currentNamespace?: string[]): Set<string> {
        const symbols = new Set<string>();
        const scopeStack: ts.Node[] = [];
        let currentNode: ts.Node | undefined = node;

        // 构建作用域栈
        while (currentNode) {
            scopeStack.push(currentNode);
            currentNode = currentNode.parent;
        }

        // 遍历作用域栈，收集符号
        scopeStack.reverse().forEach((scopeNode) => {
            if (ts.isVariableStatement(scopeNode)) {
                scopeNode.declarationList.declarations.forEach((declaration) => {
                    if (ts.isIdentifier(declaration.name)) {
                        const symbol = typeChecker.getSymbolAtLocation(declaration.name);
                        if (symbol) {
                            symbols.add(symbol.name);
                        }
                    }
                });
            } else if (ts.isFunctionDeclaration(scopeNode) && scopeNode.name) {
                symbols.add(scopeNode.name.text);
            } else if (ts.isTypeAliasDeclaration(scopeNode) && scopeNode.name) {
                symbols.add(scopeNode.name.text);
            } else if (ts.isEnumDeclaration(scopeNode)) {
                symbols.add(scopeNode.name.text);
                scopeNode.members.forEach((member) => {
                    if (ts.isIdentifier(member.name)) {
                        symbols.add(member.name.text);
                    }
                });
            } else if (ts.isModuleBlock(scopeNode)) {
                // 处理模块块内的声明
                ts.forEachChild(scopeNode, (child) => {
                    if (ts.isVariableStatement(child)) {
                        child.declarationList.declarations.forEach((declaration) => {
                            if (ts.isIdentifier(declaration.name)) {
                                const symbol = typeChecker.getSymbolAtLocation(declaration.name);
                                if (symbol) {
                                    symbols.add(symbol.name);
                                }
                            }
                        });
                    } else if (ts.isFunctionDeclaration(child) && child.name) {
                        symbols.add(child.name.text);
                    } else if (ts.isTypeAliasDeclaration(child) && child.name) {
                        symbols.add(child.name.text);
                    } else if (ts.isEnumDeclaration(child)) {
                        symbols.add(child.name.text);
                        child.members.forEach((member) => {
                            if (ts.isIdentifier(member.name)) {
                                symbols.add(member.name.text);
                            }
                        });
                    }
                });
            }
        });

        // 添加当前命名空间及其父级命名空间内的符号
        if (currentNamespace) {
            let currentNsSymbol = typeChecker.getSymbolAtLocation(sourceFile);
            for (let i = 0; i < currentNamespace.length; i++) {
                if (currentNsSymbol) {
                    const exports = typeChecker.getExportsOfModule(currentNsSymbol);
                    for (const exportSymbol of exports) {
                        if (exportSymbol.name === "default") continue;
                        symbols.add(exportSymbol.name);
                    }
                    if (currentNsSymbol && (currentNsSymbol.flags & ts.SymbolFlags.Alias)) {
                        currentNsSymbol = typeChecker.getAliasedSymbol(currentNsSymbol);
                    }
                }
            }
        }

        return symbols;
    }

    // 检查符号是否可以在当前作用域直接引用
    private canBeReferencedWithoutQualifier(symbol: ts.Symbol, currentScopeSymbols: Set<string>, currentNamespace?: string[]): boolean {
        if (!currentNamespace) {
            return currentScopeSymbols.has(symbol.name);
        }

        // 检查符号是否在当前命名空间或其父级命名空间中声明
        const symbolDeclarations = symbol.declarations || [];
        for (const declaration of symbolDeclarations) {
            if (ts.isModuleDeclaration(declaration)) {
                const declNamespace = this.getNamespacePath(declaration);
                if (declNamespace && this.isSubNamespace(declNamespace, currentNamespace)) {
                    return true;
                }
            } else {
                // 检查符号是否在当前作用域内声明
                if (currentScopeSymbols.has(symbol.name)) {
                    return true;
                }
            }
        }

        return false;
    }

    // 获取命名空间路径
    private getNamespacePath(moduleDecl: ts.ModuleDeclaration): string[] | undefined {
        const path: string[] = [];
        let current: ts.Node | undefined = moduleDecl;
        while (current) {
            if (ts.isModuleDeclaration(current)) {
                path.unshift(current.name.getText());
            }
            // 安全地获取父节点
            current = this.getModuleParent(current);
        }
        return path.length > 0 ? path : undefined;
    }

    // 获取模块声明的父级模块声明或源文件
    private getModuleParent(node: ts.Node): ts.Node | undefined {
        let parent = node.parent;
        while (parent) {
            if (ts.isModuleDeclaration(parent) || ts.isSourceFile(parent)) {
                return parent;
            }
            parent = parent.parent;
        }
        return undefined;
    }

    // 检查一个命名空间路径是否是另一个命名空间路径的子命名空间
    private isSubNamespace(path1: string[], path2: string[]): boolean {
        if (path1.length > path2.length) {
            return false;
        }
        for (let i = 0; i < path1.length; i++) {
            if (path1[i] !== path2[i]) {
                return false;
            }
        }
        return true;
    }
    // 创建一个 TypeScript 程序
    private createProgram(filePath: string): ts.Program {
        return ts.createProgram([filePath], {});
    }

    // 遍历过滤后的 locations 并执行 addIssueReportNodeFix
    private filteredLocations(locations: LocationInfo[]): LocationInfo[] {
        // 过滤 locations，保留 line 和 startCol 相同但 endCol 最大的 LocationInfo 对象
        const filteredLocations: LocationInfo[] = [];
        locations.forEach((loc) => {
            let found = false;
            for (let i = 0; i < filteredLocations.length; i++) {
                const existingLoc = filteredLocations[i];
                if (existingLoc.line === loc.line && existingLoc.startCol === loc.startCol) {
                    if (loc.endCol > existingLoc.endCol) {
                        filteredLocations[i] = loc; // 更新记录
                    }
                    found = true;
                    break;
                }
            }
            if (!found) {
                filteredLocations.push(loc); // 添加新记录
            }
        });
        return filteredLocations;
    }

    // 创建修复对象 
    private ruleFix(loc: LocationInfo): RuleFix {
        return { range: [loc.startCol, loc.endCol], text: '' };
    }

    private addIssueReportNodeFix(loc: LocationInfo, filePath: string) {
        const severity = this.rule.alert ?? this.metaData.severity;
        this.metaData.description = "Qualifier is unnecessary since '" + loc.Qualifier + "' is in scope";
        let defect = new Defects(loc.line, loc.startCol, loc.endCol, this.metaData.description, severity,
            this.rule.ruleId, filePath, this.metaData.ruleDocPath, true, false, false);
        let fix: RuleFix = this.ruleFix(loc);
        this.issues.push(new IssueReport(defect, fix));
        RuleListUtil.push(defect);
    }
}